#include "sys_spi.h"
#include "sys_types.h"
#include "sys_uart.h"
#include "sys_interrupt.h"
#include "sys_delay.h"
#include "sys_gpio.h"
#include "sys_io.h"
#include "sys_cache.h"
#include "sys_clock.h"
#include "sys_dma.h"
#include "stdlib.h"

#ifdef __SPI_DMA_EN // SPI DMA??
#define SPI_DMA_EN 1
#endif

// 默认最大时钟频率
#define SPI_MAX_CLK_HZ (60000000)
/*DMA使能时需要使用到内存分配，如果有使用系统,不能使用C库的malloc,请使用固定内存分配或系统的内存管理*/

/*---------------------------------------------------
修改记录
2020-04-13
	1.128x512字节写不正常，sys_spi.c中SPI_Flash_Write函数的参数unsigned short NumByteToWrite改为unsigned int NumByteToWrite
2020-08-19
	1.增加DMA读写模式
2020-12-17
	1.增加32K块，64K块擦除函数
	2.增加读UID(唯一ID),设备ID函数
2021-06-10
	1.DMA与中断开启的情况读写失败：增加一个虚拟缓存virtual_buff替换读写函数的NULL代入参数。
2021-07-07
	1.virtual_buff改为使用malloc分配，如果有使用系统,不能使用C库的malloc,请使用固定内存分配或系统的内存管理。
2021-07-09
	1.增加对16MB以上型号读写的支持,主要增加进入4地址模式与4地址读写-W25Q256测试正常。
2021-07-14
	1.增加对CLK的频率限制与最大频率设置函数,避免频率太高而读写错误问题。


----------------------------------------------------*/
#define spi_get_addr(x) (0x01C05000 + x * 0x1000) // 返回SPI地址

// HZ 最大时钟限制
static int spi_max_clk = SPI_MAX_CLK_HZ;

static DMA TXdma;
static DMA RXdma;
static DMA *SPI_TXdma = &TXdma;
static DMA *SPI_RXdma = &RXdma;

static uint8_t dma_enable = 0;

enum
{
	SPI_GCR = 0x04,
	SPI_TCR = 0x08,
	SPI_IER = 0x10,
	SPI_ISR = 0x14,
	SPI_FCR = 0x18,
	SPI_FSR = 0x1c,
	SPI_WCR = 0x20,
	SPI_CCR = 0x24,
	SPI_MBC = 0x30,
	SPI_MTC = 0x34,
	SPI_BCC = 0x38,
	SPI_TXD = 0x200,
	SPI_RXD = 0x300,
};

void *spi_malloc(size_t size)
{
	void *p = malloc(size);
	if (((int)p % 4) != 0) // DMA缓存需要4字节对齐
	{
		sysprintf("ERR:spi_malloc() alignment(%08x)\r\n", p);
		free(p);
		return 0;
	}
	return p;
}

void spi_free(void *p)
{
	free(p);
}

void sys_spi_set_max_clk(int clk)
{
	spi_max_clk = clk;
}

/*---------------------------------------------------
SPI DMA传输时的等待->多任务时可执行其它任务
---------------------------------------------------*/
void spi_dma_delay(void)
{
	// delay_ms(1);
}
/*---------------------------------------------------
SPI配置
---------------------------------------------------*/
void spi_spi_confing(int SPI)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	u32_t val;

	/*
	if CPU_CLK=600MHZ
	AHB_CLK=CPU_CLK/2=600/2=300
	SPI_CLK=AHB_CLK / (2*(n + 1));
	[n=2 SPI_CLK=50MHZ]
	[n=1 SPI_CLK=75MHZ]
	[n=0 SPI_CLK=150MHZ]
	*/

	/*
	if CPU_CLK=408MHZ
	AHB_CLK=CPU_CLK/2=408/2=204
	SPI_CLK=AHB_CLK / (2*(n + 1));


	[n=5 SPI_CLK=17MHZ]
	[n=4 SPI_CLK=20.4MHZ]
	[n=3 SPI_CLK=25.5MHZ]
	[n=2 SPI_CLK=34MHZ]
	[n=1 SPI_CLK=51MHZ]
	[n=0 SPI_CLK=102MHZ]
	*/
	int n = 0;
	int ahb_clk = get_ahb_frequency();
	while (n < 10)
	{
		int spi_clk = ahb_clk / (2 * (n + 1));
		if (spi_clk > spi_max_clk) // SPI 时钟频率限制
		{
			sysprintf("SPI%d CLK SET.\r\n", SPI, n);
			n++;
		}
		else
		{
			sysprintf("SPI%d CLK[%dHZ]...\r\n", SPI, spi_clk);
			break;
		}
	}
	/* Set spi clock rate control register*/
	write32(addr + SPI_CCR, 0x00001000 | n);

	/* Enable spi and do a soft reset */
	val = read32(addr + SPI_GCR);
	val |= ((u64_t)1 << 31) | (1 << 7) | (1 << 1) | (1 << 0);
	write32(addr + SPI_GCR, val);
	while (read32(addr + SPI_GCR) & ((u64_t)1 << 31))
		;

	val = read32(addr + SPI_TCR);
	val &= ~(0x3 << 0);
	val |= (1 << 6) | (1 << 2);
	write32(addr + SPI_TCR, val);

	val = read32(addr + SPI_FCR);
	val |= ((u64_t)1 << 31) | (1 << 15);
	write32(addr + SPI_FCR, val);
}

/*---------------------------------------------------
SPI开CS
---------------------------------------------------*/
void sys_spi_select(int SPI)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	u32_t val;

	val = read32(addr + SPI_TCR);
	val &= ~((0x3 << 4) | (0x1 << 7));
	val |= ((0 & 0x3) << 4) | (0x0 << 7);
	write32(addr + SPI_TCR, val);
}
/*---------------------------------------------------
SPI关CS
---------------------------------------------------*/
void sys_spi_deselect(int SPI)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	u32_t val;

	val = read32(addr + SPI_TCR);
	val &= ~((0x3 << 4) | (0x1 << 7));
	val |= ((0 & 0x3) << 4) | (0x1 << 7);
	write32(addr + SPI_TCR, val);
}
/*---------------------------------------------------
SPI写
---------------------------------------------------*/
static void sys_spi_write_txbuf(int SPI, u8_t *buf, int len)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	int i;

	if (!buf)
		len = 0;

	write32(addr + SPI_MTC, len & 0xffffff);
	write32(addr + SPI_BCC, len & 0xffffff);
	for (i = 0; i < len; ++i)
		write8(addr + SPI_TXD, *buf++);
}
/*---------------------------------------------------
使能DMA_DRQ
---------------------------------------------------*/
void sys_spi_enable_drq(int SPI, int tx_drq_en, int rx_drq_en)
{
	virtual_addr_t addr = spi_get_addr(SPI);

	if (tx_drq_en == 0)
		C_BIT(addr + SPI_FCR, 24); // TX_DRQ
	else
		S_BIT(addr + SPI_FCR, 24);

	if (rx_drq_en == 0)
		C_BIT(addr + SPI_FCR, 8); // RX_DRQ
	else
		S_BIT(addr + SPI_FCR, 8);
}
/*---------------------------------------------------
设置计数值
---------------------------------------------------*/
void sys_spi_set_count(int SPI, int count)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	write32(addr + SPI_MBC, count & 0xffffff);
	write32(addr + SPI_MTC, count & 0xffffff);
	write32(addr + SPI_BCC, count & 0xffffff);
}
/*---------------------------------------------------
SPI开始
---------------------------------------------------*/
void sys_spi_start(int SPI)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	S_BIT(addr + SPI_TCR, 31);
}
/*---------------------------------------------------
SPI SPI CPU传输
----------------------------------------------------*/
static int sys_spi_cpu_transfer(int SPI, void *txbuf, void *rxbuf, int len)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	int count = len;
	u8_t *tx = txbuf;
	u8_t *rx = rxbuf;
	u8_t val;
	int n, i;
	while (count > 0)
	{
		n = (count <= 64) ? count : 64;
		write32(addr + SPI_MBC, n);
		sys_spi_write_txbuf(SPI, tx, n);
		write32(addr + SPI_TCR, read32(addr + SPI_TCR) | ((u64_t)1 << 31));

		while ((read32(addr + SPI_FSR) & 0xff) < n)
			;

		for (i = 0; i < n; i++)
		{
			val = read8(addr + SPI_RXD);
			if (rx)
				*rx++ = val;
		}
		if (tx)
			tx += n;
		count -= n;
	}
	return len;
}

/*---------------------------------------------------
SPI DMA transfe
----------------------------------------------------*/
int sys_spi_dma_transfer(int SPI, DMA *TXdma, DMA *RXdma, void *txbuf, void *rxbuf, int len)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	//---------
	TXdma->Type = NDMA;					 // 类型
	TXdma->Ch = 0;						 // 通道
	TXdma->Byte_Counter = len;			 // Byte计数
	TXdma->Continuous_Mode_Enable = 0;	 // 连续模式
	TXdma->Read_Byte_Counter_Enable = 0; // 读计数值使能
	//---------
	TXdma->Source_Address = (unsigned int)txbuf;		  // 源地址
	TXdma->Source_Address_Type = DMA_ADDRESS_TYEP_LINEER; // 源地址类型
	TXdma->Source_DRQ_Type = NDMAS_DRQ_Type_SDRAM_Memory; // 源类型
	TXdma->Source_Data_Width = DMA_DATA_WIDTH_8;		  // 源数据宽度
	TXdma->Source_Burst_Length = DMA_BURST_LENGTH_1;	  // BURST
	//-----------
	TXdma->Destination_Address = (unsigned int)(addr + SPI_TXD);								// 目标地址
	TXdma->Destination_Address_Type = DMA_ADDRESS_TYEP_IO;										// 目标地址类型
	TXdma->Destination_DRQ_Type = (SPI == 0 ? NDMAD_DRQ_Type_SPI0_Tx : NDMAD_DRQ_Type_SPI1_Tx); // 目标类型
	TXdma->Destination_Data_Width = DMA_DATA_WIDTH_8;											// 目标数据宽度
	TXdma->Destination_Burst_Length = DMA_BURST_LENGTH_1;										// BURST
																								//-----------
	// sysprintf("发送数据大小: %d \r\n", TXdma->Byte_Counter);
	DMA_Config(TXdma);
	DMA_Enable(TXdma);

	//---------------------------------------------------------------------------------------
	RXdma->Type = NDMA;					 // 类型
	RXdma->Ch = 1;						 // 通道
	RXdma->Byte_Counter = len;			 // Byte计数
	RXdma->Continuous_Mode_Enable = 0;	 // 连续模式
	RXdma->Read_Byte_Counter_Enable = 0; // 读计数值使能
	//---------
	RXdma->Source_Address = (unsigned int)(addr + SPI_RXD);								   // 源地址
	RXdma->Source_Address_Type = DMA_ADDRESS_TYEP_IO;									   // 源地址类型
	RXdma->Source_DRQ_Type = (SPI == 0 ? NDMAS_DRQ_Type_SPI0_Rx : NDMAS_DRQ_Type_SPI1_Rx); // 源类型
	RXdma->Source_Data_Width = DMA_DATA_WIDTH_8;										   // 源数据宽度
	RXdma->Source_Burst_Length = DMA_BURST_LENGTH_1;									   // BURST
	//-----------
	RXdma->Destination_Address = (unsigned int)rxbuf;		   // 目标地址
	RXdma->Destination_Address_Type = DMA_ADDRESS_TYEP_LINEER; // 目标地址类型
	RXdma->Destination_DRQ_Type = NDMAD_DRQ_Type_SDRAM_Memory; // 目标类型
	RXdma->Destination_Data_Width = DMA_DATA_WIDTH_8;		   // 目标数据宽度
	RXdma->Destination_Burst_Length = DMA_BURST_LENGTH_1;	   // BURST
	//-----------
	// sysprintf("接收数据大小: %d \r\n", RXdma->Byte_Counter);
	DMA_Config(RXdma);
	DMA_Enable(RXdma);

	// 设置计数值
	sys_spi_set_count(SPI, len);
	// 开启DMA_DRQ
	sys_spi_enable_drq(SPI, 1, 1);
	// 片选使能
	// sys_spi_select(SPI);
	// SPI开始
	sys_spi_start(SPI);
	//----
	// sysprintf("等待发送 \r\n");
	while (1)
	{
		if (DMA_Get_Full_TIP(RXdma))
		{
			// sysprintf("发送数据完成...\r\n");
			break;
		}
		// 延时或作务
		spi_dma_delay();
	}
	DMA_Disable(RXdma);
	DMA_Disable(TXdma);
	// 写通CACHE
	//	sysInvalidCache();
	sysFlushCache(D_CACHE);
	// 片选失能
	//	sys_spi_deselect(SPI);
	//--
	return 0;
}

/*SPI传输
 */
int sys_spi_transfer(int SPI, void *txbuf, void *rxbuf, int len)
{
	void *p = 0;
	if (dma_enable && len > 64) //>64 DMA模式-DMA模式时缓存指针不能为0
	{
		if (txbuf == NULL)
		{
			p = spi_malloc(len);
			if (p == 0)
			{
				sysprintf("ERR:spi_malloc() txbuf==0 \r\n");
				return 0;
			}
			txbuf = p;
		}
		if (rxbuf == NULL)
		{
			p = spi_malloc(len);
			if (p == 0)
			{
				sysprintf("ERR:spi_malloc() rxbuf==0 \r\n");
				return 0;
			}
			rxbuf = p;
		}
		if (((int)txbuf % 4) != 0) // DMA缓存需要4字节对齐
		{
			sysprintf("ERR:spi_malloc() txbuf alignment(%08x)\r\n", txbuf);
			return 0;
		}
		if (((int)rxbuf % 4) != 0) // DMA缓存需要4字节对齐
		{
			sysprintf("ERR:spi_malloc() rxbuf alignment(%08x)\r\n", rxbuf);
			return 0;
		}

		sys_spi_dma_transfer(SPI, SPI_TXdma, SPI_RXdma, txbuf, rxbuf, len);
		if (p)
		{
			spi_free(p);
		}
	}
	else // CPU模式
	{
		sys_spi_cpu_transfer(SPI, txbuf, rxbuf, len);
	}
	return len;
}

/*SPI write_then_read
 */
int sys_spi_write_then_read(int SPI, void *txbuf, int txlen, void *rxbuf, int rxlen)
{
	if (sys_spi_transfer(SPI, txbuf, NULL, txlen) != txlen)
	{
		return -1;
	}
	if (sys_spi_transfer(SPI, NULL, rxbuf, rxlen) != rxlen)
	{
		return -1;
	}
	return 0;
}
/*SPI write_then_write
 */
int sys_spi_write_then_write(int SPI, void *txbuf0, int txlen0, void *txbuf1, int txlen1)
{
	if (sys_spi_transfer(SPI, txbuf0, NULL, txlen0) != txlen0)
	{
		return -1;
	}
	if (sys_spi_transfer(SPI, txbuf1, NULL, txlen1) != txlen1)
	{
		return -1;
	}
	return 0;
}

/*SPI IO初始化
 */
void spi_gpio_init(int SPI)
{
	int i = 0;
	if (SPI == SPI0)
	{
		for (i = 0; i < 4; i++)
		{
			GPIO_Congif(GPIOC, GPIO_Pin_0 + i, GPIO_Mode_010, GPIO_PuPd_NOPULL);
			GPIO_Multi_Driving(GPIOC, GPIO_Pin_0 + i, 3); // 增加驱动力
		}
	}
	else if (SPI == SPI1)
	{
		for (i = 0; i < 4; i++)
		{
			GPIO_Congif(GPIOE, GPIO_Pin_7 + i, GPIO_Mode_100, GPIO_PuPd_NOPULL);
			GPIO_Multi_Driving(GPIOE, GPIO_Pin_7 + i, 3); // 增加驱动力
		}
	}
}
/*SPI clock_初始化
 */
void spi_clock_init(int SPI)
{
	S_BIT(0x01c202c0, (20 + SPI)); /* Deassert reset */
	S_BIT(0x01c20060, (20 + SPI)); /* Open the bus gate */
}

/*SPI初始化
 */
void sys_spi_init(int SPI)
{
	spi_gpio_init(SPI);
	spi_clock_init(SPI);
	spi_spi_confing(SPI);

	/*------DMA初始化------*/
	DMA_Init();
	dma_enable = 0;
}
